<template>
  <el-container class="main-container full-height">
    <el-container>
      <div class="mc-right" :style="{ width: visible ? width : '41px' }">
        <div class="saber-left">
          <div
            :class="{ 'saber-left-active': active === 'com' }"
            @click="(active = 'com'), (visible = true)"
          >
            组件
          </div>
          <div
            :class="{ 'saber-left-active': active === 'temp' }"
            @click="(active = 'temp'), (visible = true)"
          >
            模板
          </div>
          <div
            :class="{ 'saber-left-active': active === 'data' }"
            @click="(active = 'data'), (visible = true)"
          >
            数据源
          </div>
          <div
            :class="{ 'saber-left-active': active === 'history' }"
            @click="(active = 'history'), (visible = true)"
          >
            <svg-icon
              :icon-class="active === 'history' ? 'history-active' : 'history'"
              class-name="color-svg-icon"
            />
            历史记录
          </div>
          <div
            :class="{ 'saber-left-active': active === 'page' }"
            @click="(active = 'page'), (visible = true)"
          >
            <svg-icon icon-class="page" class-name="color-svg-icon" />
            页面
          </div>
          <div
            :class="{ 'saber-left-active': active === 'layout' }"
            @click="(active = 'layout'), (visible = true)"
          >
            <svg-icon icon-class="page" class-name="color-svg-icon" />
            布局
          </div>
        </div>
        <transition name="fade" tag="div">
          <div class="con-right" v-if="visible">
            <component-box
              v-if="active === 'com'"
              ref="componentSourceBoxRef"
              :designer="designer"
              :editComponent="editComponent"
            ></component-box>
            <data-source-box
              v-if="active === 'data'"
              :designer="designer"
              :width="width"
            ></data-source-box>
            <template-source-box
              v-if="active === 'temp'"
              ref="templateSourceBoxRef"
              :loadTempLate="loadTempLate"
              :designer="designer"
              :width="width"
            ></template-source-box>
            <history-box
              v-if="active === 'history'"
              ref="historyBoxRef"
              :designer="designer"
              :width="width"
              :reloadPage="reloadPage"
              :rowData="rowData"
            ></history-box>
            <page-box
              v-if="active === 'page'"
              ref="pageBoxRef"
              :designer="designer"
              :reloadPage="reloadPage"
              :width="width"
              :rowData="rowData"
            ></page-box>
          </div>
        </transition>
      </div>

      <el-container class="center-layout-container">
        <el-header class="toolbar-header">
          <toolbar-panel
            :designer="designer"
            :global-dsv="globalDsv"
            :name="rowData.nameCn"
            @visible="visible = !visible"
            @saveJson="(obj) => $emit('saveJson', obj)"
            :visible="visible"
            ref="toolbarRef"
          >
            <template v-for="(idx, slotName) in $slots" #[slotName]>
              <slot :name="slotName"></slot>
            </template>
          </toolbar-panel>
        </el-header>
        <el-main class="form-widget-main" id="form-widget-main">
          <v-form-widget
            :designer="designer"
            :form-config="designer.formConfig"
            :global-dsv="globalDsv"
            :dataSourceMapData="dataSourceMapData"
            :script="pScript"
            :refreshTemplate="refreshTemplate"
            :refreshComponent="refreshComponent"
            ref="formRef"
          >
          </v-form-widget>
        </el-main>
      </el-container>

      <div style="width: 370px; border-left: 1px solid #ddd">
        <setting-panel
          :designer="designer"
          :rowData="rowData"
          :formConfig="designer.formConfig"
          :selected-widget="designer.selectedWidget"
        ></setting-panel>
      </div>
    </el-container>
  </el-container>
</template>

<script>
import SvgIcon from "@/components/svg-icon/index.vue";

import WidgetPanel from "./widget-panel/index";
import ToolbarPanel from "./toolbar-panel/index";
import SettingPanel from "./setting-panel/index";
import VFormWidget from "./form-widget/index";
import { createDesigner } from "@/components/form-designer/designer";
import {
  addWindowResizeHandler,
  deepClone,
  getAllContainerWidgets,
  getAllFieldWidgets,
  getQueryParam,
  traverseAllWidgets,
} from "@/utils/util";
import { MOCK_CASE_URL, VARIANT_FORM_VERSION } from "@/utils/config";
import i18n, { changeLocale } from "@/utils/i18n";
import axios from "axios";
import MsDrawer from "../../components/ms-drawer/ms-drawer-base.vue";
import source_script from "@/components/javaScript/source_script";
import DraggableTemplate from "@/components/form-designer/widget-panel/draggable-template.vue";
import ComponentBox from "@/components/form-designer/widget-panel/component-box.vue";
import HistoryBox from "@/components/form-designer/widget-panel/history-box.vue";
import PageBox from "@/components/form-designer/widget-panel/page-box.vue";
import DataSourceBox from "@/components/form-designer/widget-panel/data-source-box.vue";
import TemplateSourceBox from "@/components/form-designer/widget-panel/template-source-box.vue";
import * as entityApi from "@/api/modulManage/entity";
import * as appApi from "@/api/applicationManage/applicationManage";

export default {
  name: "VFormDesigner",
  componentName: "VFormDesigner",
  mixins: [i18n, source_script],
  components: {
    DataSourceBox,
    TemplateSourceBox,
    ComponentBox,
    DraggableTemplate,
    MsDrawer,
    WidgetPanel,
    ToolbarPanel,
    SettingPanel,
    VFormWidget,
    SvgIcon,
    HistoryBox,
    PageBox,
  },
  props: {
    /* 当前列数据 */
    rowData: {
      type: Object,
      default: {},
    },
    /* 后端字段列表API */
    fieldListApi: {
      type: Object,
      default: null,
    },

    /* 禁止显示的组件名称数组 */
    bannedWidgets: {
      type: Array,
      default: () => [],
    },
    /* 编辑组件 */
    editComponent: {
      type: Function,
      default: () => {},
    },
    /* 编辑组件 */
    reloadPage: {
      type: Function,
      default: () => {},
    },

    designerConfig: {
      type: Object,
      default: () => {
        return {
          languageMenu: true, //是否显示语言切换菜单
          externalLink: true, //是否显示GitHub、文档等外部链接
          formTemplates: false, //是否显示表单模板
          eventCollapse: true, //是否显示组件事件属性折叠面板
          widgetNameReadonly: false, //禁止修改组件名称

          clearDesignerButton: true, //是否显示清空设计器按钮
          previewFormButton: true, //是否显示预览表单按钮
          importJsonButton: true, //是否显示导入JSON按钮
          exportJsonButton: true, //是否显示导出JSON器按钮
          exportCodeButton: true, //是否显示导出代码按钮
          generateSFCButton: true, //是否显示生成SFC按钮
          toolbarMaxWidth: 420, //设计器工具按钮栏最大宽度（单位像素）
          toolbarMinWidth: 300, //设计器工具按钮栏最小宽度（单位像素）

          presetCssCode: "", //设计器预设CSS样式代码

          resetFormJson: true, //是否在设计器初始化时将表单内容重置为空
        };
      },
    },

    /* 全局数据源变量 */
    globalDsv: {
      type: Object,
      default: () => ({}),
    },
  },
  data() {
    return {
      vFormVersion: VARIANT_FORM_VERSION,
      curLangName: "",
      vsCodeFlag: false,
      caseName: "",

      docUrl: "https://www.vform666.com/document.html",
      gitUrl: "https://github.com/vform666/variant-form",
      chatUrl: "https://www.vform666.com/pages/chat-group/",
      subScribeUrl: "https://www.vform666.com/pages/pro/",

      scrollerHeight: 0,

      designer: createDesigner(this),

      fieldList: [],

      visible: true,
      width: "350px",
      common: {},

      pScript: {
        data: {},
        methods: [],
      },

      tempScript: {},

      active: "com",
    };
  },
  provide() {
    return {
      serverFieldList: this.fieldList,
      getDesignerConfig: () => this.designerConfig,
      getBannedWidgets: () => this.bannedWidgets,
    };
  },
  created() {
    this.vsCodeFlag = getQueryParam("vscode") == 1;
    this.caseName = getQueryParam("case");
  },
  mounted() {
    let that = this;
    document.addEventListener("keydown", function (event) {
      let keyCode = event.keyCode;
      if (keyCode === 91 || keyCode === 17) {
        that.designer.startSelect = true;
      }
    });
    document.addEventListener("keyup", function () {
      that.designer.startSelect = false;
    });

    this.initLocale();

    this.scrollerHeight = window.innerHeight - 56 - 36 + "px";
    addWindowResizeHandler(() => {
      this.$nextTick(() => {
        this.scrollerHeight = window.innerHeight - 56 - 36 + "px";
      });
    });

    this.loadCase();

    this.loadFieldListFromServer();
    this.getEntityList();
    this.getComponentList();
  },
  methods: {
    // 若是根据实体生成的页面，则根据实体渲染 data-view组件
    addWidgetByEntity() {
      if (!this.rowData.json && this.rowData.entity) {
        const dataView = this.designer.copyNewContainerWidget(
          this.designer.getContainerByType("data-view")
        );
        // this.designer.addDataScript(dataView);
        dataView.options.entity = this.rowData.entity;
        this.designer.widgetList = [dataView];
        this.designer.setSelected(dataView);
      }
    },
    // 获取自定义组件
    getComponentList() {
      appApi.componentPage(1, 1000).then((res) => {
        const compList = res.data.data.list || [];
        const comObj = {};
        if (compList && compList.length > 0) {
          compList.forEach((v) => {
            comObj[v.code] = v.json;
          });
          this.designer.selfComponentObj = comObj;
        }
      });
    },
    getEntityList() {
      entityApi.from_to().then((res) => {
        this.designer.entityList = res.data.data;
        this.addWidgetByEntity();
      });
    },
    refreshTemplate() {
      this.$refs.templateSourceBoxRef &&
        this.$refs.templateSourceBoxRef.templatePage();
    },
    refreshComponent() {
      this.$refs.componentSourceBoxRef &&
        this.$refs.componentSourceBoxRef.getComponentList();
    },
    initTempScript(script) {
      if (!script || Object.keys(script).length === 0) {
        return;
      }
      for (const key in script) {
        let s = script[key];

        this.initScript(s);
      }
    },
    initScript(s) {
      if (!s) {
        return;
      }
      if (s.data) {
        this.pScript.data = JSON.parse(s.data);
      }

      if (s.methods.length > 0) {
        s.methods.forEach((item) => {
          if (item.funName && item.funBody) {
            let obj = {};
            obj[item.funName] = new Function("arguments", item.funBody);
            Object.assign(this.pScript.methods, obj);
          }
        });
      }
    },

    openUrl(event, url) {
      if (this.vsCodeFlag) {
        const msgObj = {
          cmd: "openUrl",
          data: {
            url,
          },
        };
        window.parent.postMessage(msgObj, "*");
      } else {
        let aDom = event.currentTarget;
        aDom.href = url;
        //window.open(url, '_blank') //直接打开新窗口，会被浏览器拦截
      }
    },

    loadCase() {
      if (!this.caseName) {
        return;
      }

      axios
        .get(MOCK_CASE_URL + this.caseName + ".txt")
        .then((res) => {
          if (res.data.code) {
            this.$message.error(this.i18nt("designer.hint.sampleLoadedFail"));
            return;
          }

          this.setFormJson(res.data);
          this.$message.success(
            this.i18nt("designer.hint.sampleLoadedSuccess")
          );
        })
        .catch((error) => {
          this.$message.error(
            this.i18nt("designer.hint.sampleLoadedFail") + ":" + error
          );
        });
    },

    initLocale() {
      let curLocale = localStorage.getItem("v_form_locale");
      if (this.vsCodeFlag) {
        curLocale = curLocale || "en-US";
      } else {
        curLocale = curLocale || "zh-CN";
      }
      this.curLangName = this.i18nt("application." + curLocale);
      this.changeLanguage(curLocale);
    },

    loadFieldListFromServer() {
      if (!this.fieldListApi) {
        return;
      }

      let headers = this.fieldListApi.headers || {};
      axios
        .get(this.fieldListApi.URL, { headers: headers })
        .then((res) => {
          let labelKey = this.fieldListApi.labelKey || "label";
          let nameKey = this.fieldListApi.nameKey || "name";

          this.fieldList.splice(0, this.fieldList.length); //清空已有
          res.data.forEach((fieldItem) => {
            this.fieldList.push({
              label: fieldItem[labelKey],
              name: fieldItem[nameKey],
            });
          });
        })
        .catch((error) => {
          this.$message.error(error);
        });
    },

    handleLanguageChanged(command) {
      this.changeLanguage(command);
      this.curLangName = this.i18nt("application." + command);
    },

    changeLanguage(langName) {
      changeLocale(langName);
    },
    loadTempLate(formJson) {
      let modifiedFlag = false;
      if (formJson) {
        if (typeof formJson === "string") {
          modifiedFlag = this.designer.loadFormJson(JSON.parse(formJson));
        } else if (formJson.constructor === Object) {
          modifiedFlag = this.designer.loadFormJson(formJson);
        }

        if (modifiedFlag) {
          this.designer.emitHistoryChange();
        }
      }
    },
    // 初始化表单编辑器
    setFormJson(formJson) {
      let modifiedFlag = false;

      this.initScript(formJson.pageScript);

      this.requestMethod({ ...formJson });
      if (formJson) {
        if (typeof formJson === "string") {
          modifiedFlag = this.designer.loadFormJson(JSON.parse(formJson));
        } else if (formJson.constructor === Object) {
          modifiedFlag = this.designer.loadFormJson(formJson);
        }

        if (modifiedFlag) {
          this.designer.emitHistoryChange();
        }
      }
    },

    getFormJson() {
      return {
        widgetList: deepClone(this.designer.widgetList),
        formConfig: deepClone(this.designer.formConfig),
        pageScript: deepClone(this.designer.pageScript),
        dataSource: deepClone(this.designer.dataSource),
        templateScript: deepClone(this.designer.templateScript),
      };
    },

    clearDesigner() {
      this.$refs.toolbarRef.clearFormWidget();
    },

    /**
     * 刷新表单设计器
     */
    refreshDesigner() {
      //this.designer.loadFormJson( this.getFormJson() )  //只有第一次调用生效？？

      let fJson = this.getFormJson();
      this.designer.clearDesigner(true); //不触发历史记录变更
      this.designer.loadFormJson(fJson);
    },

    /**
     * 预览表单
     */
    previewForm() {
      this.$refs.toolbarRef.previewForm();
    },

    /**
     * 导入表单JSON
     */
    importJson() {
      this.$refs.toolbarRef.importJson();
    },

    /**
     * 导出表单JSON
     */
    exportJson() {
      this.$refs.toolbarRef.exportJson();
    },

    /**
     * 导出Vue/HTML代码
     */
    exportCode() {
      this.$refs.toolbarRef.exportCode();
    },

    /**
     * 生成SFC代码
     */
    generateSFC() {
      this.$refs.toolbarRef.generateSFC();
    },

    /**
     * 获取所有字段组件
     * @returns {*[]}
     */
    getFieldWidgets(widgetList = null) {
      return widgetList
        ? getAllFieldWidgets(widgetList)
        : getAllFieldWidgets(this.designer.widgetList);
    },

    /**
     * 获取所有容器组件
     * @returns {*[]}
     */
    getContainerWidgets(widgetList = null) {
      return widgetList
        ? getAllContainerWidgets(widgetList)
        : getAllContainerWidgets(this.designer.widgetList);
    },

    /**
     * 升级表单json，以补充最新的组件属性
     * @param formJson
     */
    upgradeFormJson(formJson) {
      if (!formJson.widgetList || !formJson.formConfig) {
        this.$message.error("Invalid form json!");
        return;
      }

      traverseAllWidgets(formJson.widgetList, (w) => {
        this.designer.upgradeWidgetConfig(w);
      });
      this.designer.upgradeFormConfig(formJson.formConfig);

      return formJson;
    },

    getWidgetRef(widgetName, showError = false) {
      return this.$refs["formRef"].getWidgetRef(widgetName, showError);
    },

    getSelectedWidgetRef() {
      return this.$refs["formRef"].getSelectedWidgetRef();
    },
  },
};
</script>

<style lang="scss" scoped>
.color-svg-icon {
  margin: 0;
  font-size: 18px;
  color: #212121;
}
.mc-right {
  border-right: 1px solid #ddd;
}

$saber-left-w: 40px;
.saber-left {
  width: $saber-left-w;
  height: 100%;
  background: #f2f2f2;
  border-right: 1px solid #ddd;
  float: left;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;

  > div {
    padding: 20px 8px;
    box-sizing: border-box;
    font-size: 16px;
    text-align: center;
    cursor: pointer;
  }

  .saber-left-active {
    background: $--color-primary;
    color: white;
    float: right;
    .color-svg-icon {
      color: White;
    }
  }
}

.con-right {
  width: calc(100% - #{$saber-left-w} - 10px);
  height: 100%;
  float: right;
}

.el-container.main-container {
  background: #fff;

  ::v-deep aside {
    /* 防止aside样式被外部样式覆盖！！ */
    margin: 0;
    padding: 0;
    background: inherit;
  }
}

.el-container.full-height {
  height: 100%;
  overflow-y: hidden;
}

.el-container.center-layout-container {
  min-width: 680px;
  margin-left: 2px;
  flex: 1;
}

.el-header.main-header {
  border-bottom: 2px dotted #ebeef5;
  height: 48px !important;
  line-height: 48px !important;
  min-width: 800px;
}

div.main-title {
  font-size: 18px;
  color: #242424;
  display: flex;
  align-items: center;
  justify-items: center;

  img {
    cursor: pointer;
    width: 36px;
    height: 36px;
  }

  span.bold {
    font-size: 20px;
    font-weight: bold;
    margin: 0 6px 0 6px;
  }

  span.version-span {
    font-size: 14px;
    color: #101f1c;
    margin-left: 6px;
  }
}

.float-left {
  float: left;
}

.float-right {
  float: right;
}

.el-dropdown-link {
  margin-right: 12px;
  cursor: pointer;
}

div.external-link a {
  font-size: 13px;
  text-decoration: none;
  margin-right: 10px;
  color: #606266;
}

.el-header.toolbar-header {
  font-size: 14px;
  border-bottom: 1px dotted #cccccc;
  height: 48px !important;
  //line-height: 42px !important;
}

.el-aside.side-panel {
  width: 260px !important;
  overflow-y: hidden;
}

.el-main.form-widget-main {
  padding: 0;

  position: relative;
  overflow: hidden;

  &::-webkit-scrollbar {
    width: 4px;
  }
}

.container-scroll-bar {
  ::v-deep .el-scrollbar__wrap,
  ::v-deep .el-scrollbar__view {
    overflow-x: hidden;
  }
}
</style>
